﻿using BaoVeThanhDia.MapFolder.Elements;
using BaoVeThanhDia.MapFolder.VisitorFolder;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;

namespace BaoVeThanhDia.MapFolder
{
    public class Map
    {
        public static Vector2 SizeOfMatrix;
        public static int[,] matrix;
        int[,] matrixtemp;
        public static Vector2 curPosi;
        public static string huong;
        Item[] items;
        int SoVatCan;
        Random rand;
        Vector2 start, start2;
        DoanDuong doanduong;
        Visitor visitor;//, checkvisitor;
        CheckVisitor checkvisitor;
        bool isCutComplete;
        int buocdiMin;
        public Map(ContentManager content)
        {
            rand = new Random();
            SizeOfMatrix = new Vector2(rand.Next(10, 13), rand.Next(15, 18));
            matrix = new int[(int)SizeOfMatrix.X, (int)SizeOfMatrix.Y];
            matrixtemp = new int[(int)SizeOfMatrix.X, (int)SizeOfMatrix.Y];
            items = new Item[(int)(SizeOfMatrix.X * SizeOfMatrix.Y)];
            SoVatCan = rand.Next(25, 35);
            LamDuong(content, 2);
            CreateKhung(content);
        }
        public void Load(ContentManager cont)
        {
            foreach (Item item in items)
            {
                item.Load(cont, rand.Next(20000));
            }
        }
        private void reSetMap(ContentManager content)
        {
            huong = "phai";
            curPosi = new Vector2(0, 0);
            for (int i = 0; i < items.Length; i++)
            {
                items[i] = new Item(1, new Vector2(i / (int)SizeOfMatrix.Y, i % (int)SizeOfMatrix.Y), content);
                matrix[i / (int)SizeOfMatrix.Y, i % (int)SizeOfMatrix.Y] = 1;
            }
        } // đưa map về trống rỗng
        private void LamDuong(ContentManager content, int sonhanh)
        {
            do
            {
                reSetMap(content);
                start = new Vector2(rand.Next(1, (int)SizeOfMatrix.X - 2), 0);
                curPosi = start;
                matrix[(int)start.X, (int)start.Y] = 0;
                TaoDuong();
                if (sonhanh == 2)
                {
                    #region tạo nhánh

                    for (int i = 0; i < items.Length; i++)
                    {
                        Vector2 temp = new Vector2(i / (int)SizeOfMatrix.Y, i % (int)SizeOfMatrix.Y);
                        matrixtemp[(int)temp.X, (int)temp.Y] = matrix[(int)temp.X, (int)temp.Y];
                    }
                    start2 = start;
                    reSetMap(content);
                    start = new Vector2(rand.Next(1, (int)SizeOfMatrix.X - 2), 0);
                    curPosi = start;
                    matrix[(int)start.X, (int)start.Y] = 0;
                    TaoDuong();
                    for (int i = 0; i < items.Length; i++)
                    {
                        Vector2 temp = new Vector2(i / (int)SizeOfMatrix.Y, i % (int)SizeOfMatrix.Y);
                        if (matrixtemp[(int)temp.X, (int)temp.Y] == 0)
                        {
                            matrix[(int)temp.X, (int)temp.Y] = 0;
                        }
                    }
                    #endregion

                    #region cắt bỏ các đoạn trùng nhau
                    isCutComplete = false;
                    do
                    {
                    }
                    while (!CatBoOThua());
                    #endregion
                }
                buocdiMin = Math.Min(getBuocDi(start, "phai", 1), getBuocDi(start2, "phai", 1));
            } while (!(isNotBad() && buocdiMin >= SizeOfMatrix.Y + 6));

            if (matrix[(int)start.X, (int)start.Y] == 1) start = start2;
            if (matrix[(int)start2.X, (int)start2.Y] == 1) start2 = start;

            for (int i = 0; i < items.Length; i++)
            {
                Vector2 temp = new Vector2(i / (int)SizeOfMatrix.Y, i % (int)SizeOfMatrix.Y);
                if (matrix[(int)temp.X, (int)temp.Y] == 0)
                {
                    items[i] = new Item(0, temp, content);
                }
            }
        } // đặt các ô đường vào ma trận
        private void TaoDuong()
        {
            for (int i = 0; i < 100; i++)
            {
                if (i >= 25) doanduong = new KhoiI("phai");
                else
                {
                    if (rand.Next(4000) % 3 == 0)
                    {
                        doanduong = new KhoiI();
                    }
                    else
                    {
                        doanduong = new KhoiL();
                    }
                }
                checkvisitor = new CheckVisitor();
                visitor = new SetElementVisitor();
                doanduong.accept(checkvisitor);
                if (checkvisitor.getRight())
                {
                    doanduong.accept(visitor);
                }
                if (checkDich(start, "phai")) break;
            }
        } // tính toán đường đi
        private bool isNotBad()
        {
            int temp = 0;
            for (int i = 0; i < (int)SizeOfMatrix.X; i++)
            {
                for (int j = 0; j < (int)SizeOfMatrix.Y; j++)
                {
                    if (matrix[i, j] == 0) temp++;
                }
            }
            if (temp > SizeOfMatrix.Y * 2) return true;
            else return false;
        } // kiểm tra độ dài ngắn của đường
        private bool CatBoOThua()
        {
            for (int i = 0; i < SizeOfMatrix.X; i++)
            {
                for (int j = 0; j < SizeOfMatrix.Y; j++)
                {
                    if (!isCutComplete)
                    {
                        if (i + 1 < SizeOfMatrix.X && j + 1 < SizeOfMatrix.Y && matrix[i, j] == 0 && matrix[i, j + 1] == 0 && matrix[i + 1, j] == 0 && matrix[i + 1, j + 1] == 0)//tìm ra ô vuông
                        {
                            matrix[i, j] = 1;
                            return false;
                        }
                        if (i < SizeOfMatrix.X - 1)//cắt ô cụt.
                        {
                            if ((i - 1 < 0 || (i - 1 >= 0 && matrix[i - 1, j] == 1))//bên trên 
                                && matrix[i, j] == 0 //nó
                                && matrix[i + 1, j] == 0 //bên dưới
                                && (j - 1 < 0 || (j - 1 >= 0 && matrix[i, j - 1] == 1))//bên trái
                                && (j + 1 >= SizeOfMatrix.Y || (j - 1 < SizeOfMatrix.Y && matrix[i, j + 1] == 1))//bên phải
                                )
                            {
                                matrix[i, j] = 1;
                                return false;
                            }
                        }
                    }
                    else
                    {
                        if (i + 3 < SizeOfMatrix.X)
                        {
                            if (matrix[i, j] == 0 && matrix[i + 1, j] == 0 && matrix[i + 2, j] == 1 && matrix[i + 3, j] == 0)
                            {
                                matrix[i + 2, j] = 0;
                                return false;
                            }
                        }
                        if (j + 2 < SizeOfMatrix.Y && i + 1 < SizeOfMatrix.X)
                        {
                            if (matrix[i, j] == 0 && matrix[i + 1, j + 1] == 0 && matrix[i, j + 1] == 1 && matrix[i + 1, j] == 1)
                            {
                                matrix[i, j + 1] = 0;
                                return false;
                            }
                        }
                    }
                }
            }
            if (!isCutComplete)
            {
                isCutComplete = true;
                return false;
            }
            return true;
        }
        public bool checkDich(Vector2 posi, string huongdadi)
        {
            bool isDiDcphai = false, isDiDctren = false, isDiDcduoi = false;
            if (posi.Y >= SizeOfMatrix.Y - 1) return true; // nếu là đích
            if (matrix[(int)posi.X, (int)posi.Y + 1] == 0)//kiểm tra hướng phải
            {
                isDiDcphai = true;
            }
            if (posi.X > 1 && matrix[(int)posi.X - 1, (int)posi.Y] == 0)//kiểm tra hướng trên
            {
                if (!huongdadi.Equals("duoi")) isDiDctren = true;
            }
            if (posi.X < SizeOfMatrix.X - 2 && matrix[(int)posi.X + 1, (int)posi.Y] == 0)//kiểm tra hướng dưới
            {
                if (!huongdadi.Equals("tren")) isDiDcduoi = true;
            }
            return isDiDcphai ? checkDich(posi + new Vector2(0, 1), "phai") : false
                || isDiDctren ? checkDich(posi + new Vector2(-1, 0), "tren") : false
                || isDiDcduoi ? checkDich(posi + new Vector2(1, 0), "duoi") : false;
        } // kiểm tra tại 1 điểm với đường đã đi có đến đích được ko
        private int getBuocDi(Vector2 posi, string huongdadi, int buocdadi)
        {
            bool isDiDcphai = false, isDiDctren = false, isDiDcduoi = false;
            if (posi.Y >= SizeOfMatrix.Y - 1) return buocdadi; // nếu là đích
            if (matrix[(int)posi.X, (int)posi.Y + 1] == 0)//kiểm tra hướng phải
            {
                isDiDcphai = true;
            }
            if (posi.X > 1 && matrix[(int)posi.X - 1, (int)posi.Y] == 0)//kiểm tra hướng trên
            {
                if (!huongdadi.Equals("duoi")) isDiDctren = true;
            }
            if (posi.X < SizeOfMatrix.X - 2 && matrix[(int)posi.X + 1, (int)posi.Y] == 0)//kiểm tra hướng dưới
            {
                if (!huongdadi.Equals("tren")) isDiDcduoi = true;
            }
            return Math.Min(Math.Min(isDiDcphai ? getBuocDi(posi + new Vector2(0, 1), "phai", buocdadi + 1) : 1000
                , isDiDctren ? getBuocDi(posi + new Vector2(-1, 0), "tren", buocdadi + 1) : 1000),
                isDiDcduoi ? getBuocDi(posi + new Vector2(1, 0), "duoi", buocdadi + 1) : 1000);
        } // kiểm tra đường đi ngắn nhất
        public void Draw(SpriteBatch sprite)
        {
            foreach (Item item in items)
            {
                item.Draw(sprite);
            }
        } // vẽ đường lên bản đồ
        private void CreateKhung(ContentManager content)
        {
            int temp;
            for (int i = 0; i < SoVatCan; i++)
            {
                temp = rand.Next(items.Length);
                if (matrix[temp / (int)SizeOfMatrix.Y, temp % (int)SizeOfMatrix.Y] == 1)
                {
                    items[temp] = new Item(2, new Vector2(temp / (int)SizeOfMatrix.Y, temp % (int)SizeOfMatrix.Y), content);
                    matrix[temp / (int)SizeOfMatrix.Y, temp % (int)SizeOfMatrix.Y] = 2;
                }
            }
            for (int i = 0; i < items.Length; i++)
            {
                if (i % (int)SizeOfMatrix.Y == 0 || (i + 1) % (int)SizeOfMatrix.Y == 0 || i < SizeOfMatrix.Y || i > items.Length - SizeOfMatrix.Y)
                {
                    if (matrix[i / (int)SizeOfMatrix.Y, i % (int)SizeOfMatrix.Y] == 1)
                    {
                        items[i] = new Item(2, new Vector2(i / (int)SizeOfMatrix.Y, i % (int)SizeOfMatrix.Y), content);
                        matrix[i / (int)SizeOfMatrix.Y, i % (int)SizeOfMatrix.Y] = 2;
                    }
                }
            }
        } // tung vật cản vào bản đồ
        public Vector2 getBegin()
        {
            int temp = rand.Next(10000) % 2;
            if (temp == 0) return start;
            return start2;
        }// lấy tọa độ bắt đầu
        public string XacDinhRe(Vector2 posi, string huongdadi) // xác định rẽ nhánh hay ko 
        {
            bool isDiDcphai = false, isDiDctren = false, isDiDcduoi = false;
            if (posi.Y >= SizeOfMatrix.Y - 1) return huongdadi; // nếu là đích
            if (matrix[(int)posi.X, (int)posi.Y + 1] == 0)//kiểm tra hướng phải
            {
                isDiDcphai = true;
            }
            if (posi.X > 1 && matrix[(int)posi.X - 1, (int)posi.Y] == 0)//kiểm tra hướng trên
            {
                if (!huongdadi.Equals("Bottom"))
                {
                    if (getBuocDi(posi + new Vector2(-1, 0), "tren", 5) < 1000)
                        isDiDctren = true;
                }
            }
            if (posi.X < SizeOfMatrix.X - 2 && matrix[(int)posi.X + 1, (int)posi.Y] == 0)//kiểm tra hướng dưới
            {
                if (!huongdadi.Equals("Top"))
                {
                    if (getBuocDi(posi + new Vector2(1, 0), "duoi", 5) < 1000)
                        isDiDcduoi = true;
                }
            }
            List<string> huongdi = new List<string>();
            if (isDiDcphai) huongdi.Add("Right");
            if (isDiDctren) huongdi.Add("Top");
            if (isDiDcduoi) huongdi.Add("Bottom");
            if (huongdi.Count == 0) return "Right";
            //else Debug.WriteLine("Đi được " + (isDiDcphai ? "Right" : "") + (isDiDctren ? "Top" : "") + (isDiDcduoi ? "Bot" : ""));
            int temp = rand.Next(2000) % huongdi.Count;
            int i = 0;
            foreach (string te in huongdi)
            {
                if (i == temp) return te;
                i++;
            }
            return huongdadi;
        }
    }
}
